{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "20340702",
   "metadata": {},
   "source": [
    "LangChain访问大模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "1c8b9a0f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你好！😊 很高兴见到你～有什么我可以帮忙的吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 14, 'prompt_tokens': 4, 'total_tokens': 18, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 4}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '30a0ede8-1e13-423e-81a2-83b295164df3', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--0ad6215c-5621-40ff-b07a-9870fe175022-0', usage_metadata={'input_tokens': 4, 'output_tokens': 14, 'total_tokens': 18, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "model=init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "model.invoke(\"你好\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c054792",
   "metadata": {},
   "source": [
    "使用LangGraph访问大模型，这里相当于把langchain内容封装了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa73d53e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='你好,介绍一下北京', additional_kwargs={}, response_metadata={}, id='491cf069-05f3-4f27-889d-044d39faa9ad'),\n",
       "  AIMessage(content='你好！北京是中国的首都，位于华北平原北部，拥有超过3000年的建城史和860多年的建都史，是中国历史文化与现代文明交融的国际大都市。以下是关于北京的一些关键信息：\\n\\n### **历史文化**\\n- **世界遗产**：北京拥有7项联合国教科文组织认定的世界遗产，包括故宫（明清皇家宫殿）、长城（八达岭、慕田峪等段）、天坛（明清祭天场所）、颐和园（皇家园林）、周口店北京猿人遗址等。\\n- **胡同与四合院**：老北京的传统民居和街巷格局，如南锣鼓巷、什刹海周边，展现了古都风貌。\\n\\n### **现代地标**\\n- **CBD与奥运场馆**：国贸大厦、中央电视台总部（“大裤衩”）代表现代经济活力；2008年奥运会的“鸟巢”（国家体育场）和“水立方”（国家游泳中心）已成为新地标，2022年冬奥会进一步提升了城市国际影响力。\\n- **中关村**：中国科技创新中心，聚集了众多高科技企业和顶尖学府。\\n\\n### **政治与教育**\\n- **国家行政中心**：中南海、人民大会堂等是国家政治活动核心区域。\\n- **顶尖高校**：北京大学、清华大学等位列世界知名学府。\\n\\n### **生活与文化**\\n- **美食**：北京烤鸭、炸酱面、豆汁儿等特色饮食；簋街是著名美食街区。\\n- **艺术与娱乐**：国家大剧院、798艺术区、德云社相声等丰富文化体验。\\n- **交通**：全球最繁忙的地铁系统之一，大兴国际机场（全球最大单体航站楼）与首都国际机场连接国内外。\\n\\n### **旅游推荐**\\n- **经典线路**：天安门广场（世界最大城市广场）→ 故宫 → 景山公园（俯瞰全城）→ 北海公园。\\n- **季节特色**：秋季（10月-11月）香山红叶；冬季可体验滑雪或庙会年味。\\n\\n北京既有厚重的历史沉淀，又充满创新活力，适合探索传统与现代的双重魅力。如果需要更具体的旅行或文化建议，可以进一步交流哦！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 459, 'prompt_tokens': 11, 'total_tokens': 470, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 11}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': 'c445408c-cb52-4c08-8013-474ac3f4416d', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--3c6bbed0-1c60-45fd-94ef-b6600c4bdcbe-0', usage_metadata={'input_tokens': 11, 'output_tokens': 459, 'total_tokens': 470, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "model=init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "agent=create_react_agent(\n",
    "    model=model,\n",
    "    tools=[],\n",
    "    prompt=\"你是一个小助手\"\n",
    ")\n",
    "agent.invoke({\"messages\":[{\"role\":\"user\",\"content\":\"你好,介绍一下北京\"}]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfae3cd8",
   "metadata": {},
   "source": [
    "当需要给大模型提供一些自定义的辅助工具时"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0032f1e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='' additional_kwargs={'tool_calls': [{'id': 'call_0_504211a2-9566-4e49-a4a6-bf1beb4272f2', 'function': {'arguments': '{}', 'name': 'get_current_date'}, 'type': 'function', 'index': 0}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 78, 'total_tokens': 93, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 78}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '36fed535-4d61-4c43-b4a0-8a3af2996b0f', 'service_tier': None, 'finish_reason': 'tool_calls', 'logprobs': None} id='run--54fc6b67-353c-4a54-9023-eb8f938dda8a-0' tool_calls=[{'name': 'get_current_date', 'args': {}, 'id': 'call_0_504211a2-9566-4e49-a4a6-bf1beb4272f2', 'type': 'tool_call'}] usage_metadata={'input_tokens': 78, 'output_tokens': 15, 'total_tokens': 93, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}\n",
      "[{'name': 'get_current_date', 'args': {}, 'id': 'call_0_504211a2-9566-4e49-a4a6-bf1beb4272f2', 'type': 'tool_call'}]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'今天是2025年7月17日。'"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import datetime\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain.tools import tool\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "@tool\n",
    "def get_current_date():\n",
    "    \"\"\"Get the current date.\"\"\"\n",
    "    return datetime.datetime.now().strftime(\"%Y-%m-%d\")\n",
    "#大模型绑定工具\n",
    "llm_with_tools=llm.bind_tools([get_current_date])\n",
    "#工具容器\n",
    "all_tools={\"get_current_date\":get_current_date}\n",
    "#存所有消息\n",
    "query=\"今天几月几号\"\n",
    "messages=[query]\n",
    "#询问大模型，大模型会判断所需要调用工具，并返回请求\n",
    "ai_msg=llm_with_tools.invoke(messages)\n",
    "print(ai_msg)\n",
    "messages.append(ai_msg)\n",
    "#打印需要调用的工具\n",
    "print(ai_msg.tool_calls)\n",
    "if ai_msg.tool_calls:\n",
    "    for tool_call in ai_msg.tool_calls:\n",
    "        selected_tool=all_tools[tool_call[\"name\"].lower()]\n",
    "        tool_msg=selected_tool.invoke(tool_call)\n",
    "        messages.append(tool_msg)\n",
    "llm_with_tools.invoke(messages).content\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b40af9c6",
   "metadata": {},
   "source": [
    "使用langgraph形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da18e47c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='今天是几月几号', additional_kwargs={}, response_metadata={}, id='01b8edc8-f162-4dbb-a7f5-6d09843f455c'),\n",
       "  AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_0_4ca6b46d-95f3-4283-ab65-eb9e41f884ab', 'function': {'arguments': '{}', 'name': 'get_current_date'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 77, 'total_tokens': 93, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 77}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '666ad5d7-0b76-4f57-a27b-0c35747906b4', 'service_tier': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--e9d63a19-6a4d-49c4-8680-3ed7752a98b2-0', tool_calls=[{'name': 'get_current_date', 'args': {}, 'id': 'call_0_4ca6b46d-95f3-4283-ab65-eb9e41f884ab', 'type': 'tool_call'}], usage_metadata={'input_tokens': 77, 'output_tokens': 16, 'total_tokens': 93, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       "  ToolMessage(content='2025-07-17', name='get_current_date', id='c91c88a8-6376-43a7-a3cd-a539c91df7bd', tool_call_id='call_0_4ca6b46d-95f3-4283-ab65-eb9e41f884ab'),\n",
       "  AIMessage(content='今天是2025年7月17日。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 103, 'total_tokens': 112, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 64}, 'prompt_cache_hit_tokens': 64, 'prompt_cache_miss_tokens': 39}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': 'b03acf33-6272-47a1-9ab4-86a7b28332ab', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--823d8de2-9094-49dc-ae8d-ff8950e87815-0', usage_metadata={'input_tokens': 103, 'output_tokens': 9, 'total_tokens': 112, 'input_token_details': {'cache_read': 64}, 'output_token_details': {}})]}"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "import datetime\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain.tools import tool\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "@tool\n",
    "def get_current_date():\n",
    "    \"\"\"Get the current date.\"\"\"\n",
    "    return datetime.datetime.now().strftime(\"%Y-%m-%d\")\n",
    "agent=create_react_agent(\n",
    "    model=llm,\n",
    "    tools=[get_current_date],\n",
    "    prompt=\"你是一个智能助手\"\n",
    "    )\n",
    "agent.invoke({\"messages\":[{\"role\":\"user\",\"content\":\"今天是几月几号\"}]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "daabecfd",
   "metadata": {},
   "source": [
    "# ⼀、流式输出⼤模型调⽤结果\n",
    "基于messages模式，监控⼤语⾔模型Token记录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b01a2265",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(AIMessageChunk(content='', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='湖南省', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='的', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='省', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='会是', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='**', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='长沙', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='**', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='。', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='  \\n\\n', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='长沙', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='位于', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='湖南省', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='东部', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='，', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='湘', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='江', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='下游', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='，', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='是', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='全省', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='的政治', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='、', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='经济', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='、', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='文化', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='、', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='交通', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='和', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='科教', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='中心', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='，', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='同时也是', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='中国', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='重要的', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='历史文化', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='名城', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='和', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='现代化', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='都市', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='。', additional_kwargs={}, response_metadata={}, id='run--613cb584-4fff-4299-a265-09f850f3d021'), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n",
      "(AIMessageChunk(content='', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache'}, id='run--613cb584-4fff-4299-a265-09f850f3d021', usage_metadata={'input_tokens': 12, 'output_tokens': 40, 'total_tokens': 52, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}), {'langgraph_step': 1, 'langgraph_node': 'call_model', 'langgraph_triggers': ('branch:to:call_model',), 'langgraph_path': ('__pregel_pull', 'call_model'), 'langgraph_checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'checkpoint_ns': 'call_model:1d600115-e123-c980-acdf-b9a2732df889', 'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None})\n"
     ]
    }
   ],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "from langgraph.graph import StateGraph, MessagesState, START\n",
    "from langgraph.checkpoint.memory import InMemorySaver\n",
    "def call_model(state: MessagesState):\n",
    "    response = llm.invoke(state[\"messages\"])\n",
    "    return {\"messages\": response}\n",
    "builder = StateGraph(MessagesState)\n",
    "builder.add_node(call_model)\n",
    "builder.add_edge(START, \"call_model\")\n",
    "graph = builder.compile()\n",
    "for chunk in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": \"湖南的省会是哪⾥？\"}]},\n",
    "    stream_mode=\"messages\",#记忆token的流式输出\n",
    "):\n",
    "    print(chunk)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f361ce9b",
   "metadata": {},
   "source": [
    "# ⼆、⼤模型消息持久化\n",
    "通过checkpointer构建短期记忆，以store构建⻓期记忆"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a2740ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "湖南的省会是哪⾥？\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "湖南省的省会是**长沙**。  \n",
      "\n",
      "长沙位于湖南省东部，湘江下游，是全省的政治、经济、文化、科技和交通中心，同时也是中国重要的历史文化名城和“新一线城市”。著名景点包括岳麓山、橘子洲头、马王堆汉墓等，特色美食如臭豆腐、糖油粑粑也广受欢迎。  \n",
      "\n",
      "需要了解更多关于长沙或湖南的信息吗？ 😊\n",
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "湖北呢？\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "湖北省的省会是**武汉**。  \n",
      "\n",
      "武汉位于长江与汉江交汇处，是中国中部重要的中心城市，素有“九省通衢”之称，交通地位突出。作为湖北省的政治、经济、文化中心，武汉以黄鹤楼、东湖、长江大桥等景点闻名，同时也是高等教育和科技重镇（拥有武汉大学、华中科技大学等高校）。  \n",
      "\n",
      "特色方面，武汉的**热干面**、**鸭脖**等美食全国知名，樱花季的武汉大学更是网红打卡地。  \n",
      "\n",
      "需要补充其他信息吗？ 😄\n"
     ]
    }
   ],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "from langgraph.graph import StateGraph, MessagesState, START\n",
    "from langgraph.checkpoint.memory import InMemorySaver\n",
    "def call_model(state: MessagesState):\n",
    "    response = llm.invoke(state[\"messages\"])\n",
    "    return {\"messages\": response}\n",
    "builder = StateGraph(MessagesState)\n",
    "builder.add_node(call_model)\n",
    "builder.add_edge(START, \"call_model\")\n",
    "checkpointer = InMemorySaver()\n",
    "graph = builder.compile(checkpointer=checkpointer)#进行短期记忆\n",
    "config = {#保存记忆的窗口区分id\n",
    "\"configurable\": {\n",
    "\"thread_id\": \"1\"\n",
    "}\n",
    "}\n",
    "for chunk in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": \"湖南的省会是哪⾥？\"}]},\n",
    "    config,#调用时候配置id，来带对应记忆\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    chunk[\"messages\"][-1].pretty_print()\n",
    "for chunk in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": \"湖北呢？\"}]},\n",
    "    config,\n",
    "    stream_mode=\"values\",\n",
    "    ):\n",
    "    chunk[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e327ff0",
   "metadata": {},
   "source": [
    "# 三、Human-In-Loop⼈类⼲预\n",
    "需要注意：\n",
    "- 必须指定⼀个checkpointer短期记忆，否则⽆法保存任务状态。\n",
    "在执⾏Graph任务时，必须指定⼀个带有thread_id的配置项，指定线程ID。之后才能通过线程ID，指定恢复线程。\n",
    "- 在任务执⾏过程中，通过interrupt()⽅法，中断任务，等待确认。\n",
    "- 在⼈类确认之后，使⽤Graph提交⼀个resume=True的Command指令，恢复任务，并继续进⾏\n",
    "- 任务中断和恢复，需要保持相同的thread_id。通常应⽤当中都会单独⽣成⼀个随机的thread_id，保证唯⼀的同时，防⽌其他任务⼲扰。\n",
    "- interrupt()⽅法中断任务的时间不能过⻓，过⻓了之后就⽆法恢复任务了。\n",
    "- 任务确认时，Command中传递的resume可以是简单的True或False，也可以是⼀个字典。通过字典可以进⾏更多的判断。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "1d4402a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='湖南的省会是哪⾥？', additional_kwargs={}, response_metadata={})],\n",
       " '__interrupt__': [Interrupt(value={'question': '是否同意调⽤⼤语⾔模型？'}, resumable=True, ns=['human_approval:80a6d650-45ac-bf03-378c-c3bc06ccfc8e'])]}"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 构建⼀个带有Human-In-Loop的图\n",
    "from operator import add\n",
    "from langchain_core.messages import AnyMessage\n",
    "from langgraph.checkpoint.memory import InMemorySaver\n",
    "from langgraph.constants import START, END\n",
    "from langgraph.graph import StateGraph\n",
    "from langchain.chat_models import init_chat_model\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "from typing import Literal, TypedDict, Annotated\n",
    "from langgraph.types import interrupt, Command\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list[AnyMessage], add]\n",
    "def human_approval(state: State) -> Command[Literal[\"call_llm\", END]]:\n",
    "    is_approved = interrupt(\n",
    "    {\n",
    "    \"question\": \"是否同意调⽤⼤语⾔模型？\"\n",
    "    }\n",
    "    )\n",
    "    if is_approved:\n",
    "        return Command(goto=\"call_llm\")\n",
    "    else:\n",
    "        return Command(goto=END)\n",
    "def call_llm(state:State):\n",
    "    response = llm.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [response]} \n",
    "builder = StateGraph(State)\n",
    "# Add the node to the graph in an appropriate location\n",
    "# and connect it to the relevant nodes.\n",
    "builder.add_node(\"human_approval\", human_approval)\n",
    "builder.add_node(\"call_llm\",call_llm)\n",
    "builder.add_edge(START,\"human_approval\")\n",
    "checkpointer = InMemorySaver()\n",
    "graph = builder.compile(checkpointer=checkpointer)\n",
    "from langchain_core.messages import HumanMessage\n",
    "# 提交任务，等待确认\n",
    "thread_config = {\"configurable\": {\"thread_id\": 1}}\n",
    "graph.invoke({\"messages\": [HumanMessage(\"湖南的省会是哪⾥？\")]}, config=thread_config)\n",
    "# 执⾏后会中断任务，等待确认"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "18b02bc6",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Task human_approval with path ('__pregel_pull', 'human_approval') wrote to unknown channel branch:to:__end__, ignoring it.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [HumanMessage(content='湖南的省会是哪⾥？', additional_kwargs={}, response_metadata={})]}\n"
     ]
    }
   ],
   "source": [
    "# 确认同意，继续执⾏任务\n",
    "# 不同意，终⽌任务\n",
    "final_result = graph.invoke(Command(resume=False), config=thread_config)\n",
    "print(final_result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b037c3c",
   "metadata": {},
   "source": [
    "# 四、Time Travel时间回溯\n",
    "当某⼀个步骤出现问题时，才能及时发现问题，并从发现问题的那个步骤进⾏重演。为此，LangGraph提供了Time Travel时间回溯功能，可以保存Graph的运⾏过程，并可以⼿动指定从Graph的某⼀个Node开始进⾏重演\n",
    "- 在运⾏Graph时，需要提供初始的输⼊消息。\n",
    "- 运⾏时，指定thread_id线程ID。并且要基于这个线程ID，再指定⼀个checkpoint检查点。执⾏后将在每⼀个Node执⾏后，⽣成⼀个check_point_id\n",
    "- 指定thread_id和check_point_id，进⾏任务重演。重演前，可以选择更新state，当然，如果没问题，也可以不指定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "845b4042",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<langgraph.graph.state.CompiledStateGraph object at 0x7fb9cac705c0>"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 构建⼀个图。图中两个步骤：第⼀步让⼤模型推荐⼀个有名的作家，第⼆步，让⼤模型⽤推荐的作家的⻛格写⼀个100字以内的笑话。\n",
    "from typing import TypedDict\n",
    "from typing_extensions import NotRequired\n",
    "from langgraph.checkpoint.memory import InMemorySaver\n",
    "from langgraph.constants import START, END\n",
    "from langgraph.graph import StateGraph\n",
    "from langchain.chat_models import init_chat_model\n",
    "llm =init_chat_model(\"deepseek-chat\",model_provider=\"DeepSeek\",base_url=\"https://api.deepseek.com1\",api_key=\"sk-1074763df8f14b378cda21513e24f839\")\n",
    "class State(TypedDict):\n",
    "    author: NotRequired[str]\n",
    "    joke: NotRequired[str]\n",
    "def author_node(state:State):\n",
    "    prompt = \"帮我推荐⼀位受⼈们欢迎的作家。只需要给出作家的名字即可。\"\n",
    "    author = llm.invoke(prompt)\n",
    "    return {\"author\":author}\n",
    "def joke_node(state:State):\n",
    "    prompt = f\"⽤作家：{state['author']} 的⻛格，写⼀个100字以内的笑话\"\n",
    "    joke = llm.invoke(prompt)\n",
    "    return {\"joke\":joke}\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(author_node)\n",
    "builder.add_node(joke_node)\n",
    "builder.add_edge(START,\"author_node\")\n",
    "builder.add_edge(\"author_node\",\"joke_node\")\n",
    "builder.add_edge(\"joke_node\",END)\n",
    "checkpointer = InMemorySaver()\n",
    "graph = builder.compile(checkpointer=checkpointer)\n",
    "graph\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "5d6b7ac4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='村上春树' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 4, 'prompt_tokens': 22, 'total_tokens': 26, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 22}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '18007720-5e5c-4e58-b549-0c8e698eb417', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--8fbf8ad7-f0ab-4a6b-ad47-89116878c64b-0' usage_metadata={'input_tokens': 22, 'output_tokens': 4, 'total_tokens': 26, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}\n",
      "\n",
      "content='村上春树风格的冷笑话：  \\n\\n\"早晨煮咖啡时，发现冰箱里的牛奶过期了。我盯着那个日期看了很久，突然意识到——原来牛奶也有自己的‘挪威的森林’。于是我和它默默道别，就像告别一个从未真正存在过的女孩。\"  \\n\\n（注：把日常琐事写出存在主义孤独感，搭配标志性的爵士乐/咖啡意象，最后用书名梗收尾）' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 87, 'prompt_tokens': 290, 'total_tokens': 377, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 290}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': 'c17b315f-49c0-434a-9fd2-c0a3ee8a9aad', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--0fd796fd-9b36-4e66-888e-f5c7e9a50169-0' usage_metadata={'input_tokens': 290, 'output_tokens': 87, 'total_tokens': 377, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}\n"
     ]
    }
   ],
   "source": [
    "# 正常执⾏⼀个图\n",
    "import uuid\n",
    "config = {\n",
    "\"configurable\": {\n",
    "\"thread_id\": uuid.uuid4(),#随机生成一个id避免重复\n",
    "}\n",
    "}\n",
    "state = graph.invoke({}, config)\n",
    "print(state[\"author\"])\n",
    "print()\n",
    "print(state[\"joke\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "bce9172e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "()\n",
      "1f063a01-1a8f-6f50-8002-e5db8773a73d\n",
      "\n",
      "('joke_node',)\n",
      "1f063a00-c93b-6067-8001-e4861be67882\n",
      "\n",
      "('author_node',)\n",
      "1f063a00-a312-645d-8000-622cba274ce1\n",
      "\n",
      "('__start__',)\n",
      "1f063a00-a30f-6eb3-bfff-19162e268233\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 查看所有checkpoint检查点\n",
    "states = list(graph.get_state_history(config))\n",
    "for state in states:\n",
    "    print(state.next)\n",
    "    print(state.config[\"configurable\"][\"checkpoint_id\"])\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "91365e4a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('joke_node',)\n",
      "{'author': AIMessage(content='村上春树', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 4, 'prompt_tokens': 22, 'total_tokens': 26, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 22}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '18007720-5e5c-4e58-b549-0c8e698eb417', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--8fbf8ad7-f0ab-4a6b-ad47-89116878c64b-0', usage_metadata={'input_tokens': 22, 'output_tokens': 4, 'total_tokens': 26, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}\n"
     ]
    }
   ],
   "source": [
    "# 选定某⼀个检查点。这⾥选择author_node，让⼤模型重新推荐作家，（上面的结果从下到上分别为0，1，2，3，选择1是寻找作家节点）\n",
    "selected_state = states[1]\n",
    "print(selected_state.next)\n",
    "print(selected_state.values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "9b016345",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'configurable': {'thread_id': '15ae08c2-52fc-4b7b-8f3e-47065ab4c978', 'checkpoint_ns': '', 'checkpoint_id': '1f063a09-8a6e-670c-8002-054d35ac6a30'}}\n"
     ]
    }
   ],
   "source": [
    "# 为了后⾯的重演，更新state。可选步骤：\n",
    "new_config = graph.update_state(selected_state.config, values={\"author\": \"郭德纲\"})\n",
    "print(new_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "65b9c9e6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'author': '郭德纲',\n",
       " 'joke': AIMessage(content='《德纲卖瓜》\\n\\n\"今儿卖西瓜，保熟！\"德纲蹲摊前啃瓜皮，\"您问为啥自己不吃瓜瓤？嗐！祖传手艺——说相声的能把自己说乐了，卖瓜的就不能把自个儿吃穷喽！\"\\n\\n路人要退货，他掏出发票：\"票上写\\'离柜概不负责\\'，您得念出声儿——得，包袱没响，退您五毛！\"\\n\\n（注：浓缩郭氏\"伦理哏\"与行业自嘲，98字）', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 119, 'prompt_tokens': 25, 'total_tokens': 144, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 25}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '0f856f75-c454-4269-aa52-8fd460639389', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--03ff5581-f041-45c3-b358-3cecf701935b-0', usage_metadata={'input_tokens': 25, 'output_tokens': 119, 'total_tokens': 144, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 接下来，指定thread_id和checkpoint_id，进⾏重演\n",
    "graph.invoke(None,new_config)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langgraph",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
